import { BlockInputEvents, Component, instantiate, Label, Layers, Node, Prefab, Size, Sprite, tween, UIOpacity, v3, view, Widget } from "cc";
import { App } from "../App";
import { EventConst, Singleton, ViewLayer } from "./Common";

const COMMON:string = 'Common';
export default class viewManager extends Singleton<viewManager>{
    private _rootNode:Node = null;
    private _mainView:Component = null;
    private _bottomLayer:Node = null;
    private _popLayer:Node = null;
    private _maskLayer:Node = null;
    private _topLayer:Node = null;
    private _waitNode:Node = null;
    private _waitText:Label = null;
    private _viewMap:Map<string,Map<string,Component>> = null;
    protected onInit(root:Node): void {
        if (!(root instanceof Node)){

        }
        this._viewMap = new Map<string,Map<string,Component>>();
        this._rootNode = root;
        this._bottomLayer = root.getChildByPath('Canvas/BottomLayer');
        this._popLayer = root.getChildByPath('Canvas/PopLayer');
        this._maskLayer = root.getChildByPath('Canvas/MaskLayer');
        this._topLayer = root.getChildByPath('Canvas/TopLayer');
        this._waitNode = root.getChildByPath('Canvas/MaskLayer/WaitNode');
        this._maskLayer.active = false;
        
        App.Event.on(EventConst.RESIZE_VIEWPORT,this._resizeViewport,this);
        App.Event.on(EventConst.POP_VIEW, this._openView, this);
        App.Event.on(EventConst.CLOSE_VIEW, this._closeView, this);
        App.Event.on(EventConst.LOCK_SCREEN, this._lockViewport, this);
        App.Event.on(EventConst.SET_MAIN_VIEW,this._setMainView,this);
        App.Event.on(EventConst.ENTRY_SCENE,()=>{
            this._bottomLayer.removeAllChildren();
            this._popLayer.removeAllChildren();
        },this);

        this._loadPrefab('Tips');
        this._loadPrefab('MessageBox');
        this._setMainView('HotUpdate');
    }

    public getLayer(flag:ViewLayer):Node{
        switch(flag){
            case ViewLayer.Bottom:return this._bottomLayer;
            case ViewLayer.Pop:return this._popLayer;
            case ViewLayer.Mask:return this._maskLayer;
            case ViewLayer.Top:return this._topLayer;
            default : return null;
        }
    }

    private _setMainView(name:string, ...args: any){
        new Promise(async (res)=>{
            if (!App.isInvalid(this._mainView)){
                this._bottomLayer.removeChild(this._mainView.node);
                this._mainView.destroy();
            }
            this._bottomLayer.active = true;
            const mainView:Component = await this._loadPrefab(name);
            if (!App.isInvalid(mainView)){
                App.contentSize(mainView,view.getVisibleSize());
                mainView.node.setPosition(v3(0,0,0));
                this._bottomLayer.addChild(mainView.node);
                this._mainView = mainView;
                // @ts-ignore
                mainView.init && mainView.init(args[0],args[1],args[2],args[3],args[4]);
            }
        });
    }


    private _openView(name: string, ...args: any){
        if (App.isEmpty(name)){
            console.error('viewMgr.openView is error.');
            return ;
        }

        this._popLayer.active = true;
        new Promise(async (res)=>{
            const view:Component = await this._loadPrefab(name);
            let background:Node = this._popLayer.getChildByName('Background');
            if (App.isInvalid(background)){
                background = new Node('Background');
                background.layer = Layers.Enum.UI_2D;
                background.addComponent(BlockInputEvents);
                background.addComponent(Sprite);
                App.contentSize(background,App.contentSize(this._popLayer));
                App.opacity(background,0);
                this._popLayer.addChild(background);
            } else App.opacity(background,0);
    
            background.setPosition(v3(0,0,0));
            //background.getComponent(Sprite).spriteFrame = App.Scene.saveScene();
            background.setSiblingIndex(0);
            background.active = true;
            // @ts-ignore
            view.init && view.init(args[0],args[1],args[2],args[3],args[4]);
            const opacity:UIOpacity = background.getComponent(UIOpacity);
            const opt = tween(opacity).to(0.02, {opacity: 200},{easing: 'backOut'}).call(()=>{
                view.node.scale = v3(0,0,0);
                view.node.active = true;
                this._popLayer.addChild(view.node);
                tween(view.node).to(0.01,{scale:v3(1.0,1.0,1.0)},{easing:'backInOut'})
                .call(()=>{tween(view.node).stop()}).start();
            }).call(()=>{tween(opt).stop()}).start();
        });
    }

    private _closeView(view: Node | Component){
        if (App.isInvalid(view)) return ;
        let node:Node = view instanceof Component ? view.node : view;
        tween(node).to(0.1,{scale:v3(0,0,0)},{easing:'backInOut'})
        .call(()=>{
            tween(node).stop();
            const Background:Node = this._popLayer.getChildByName('Background');
            const opt = Background.getComponent(UIOpacity);
            tween(opt)
            .to(0.01,{opacity:0},{easing:'backInOut'})
            .call(()=>{
                tween(opt).stop();
                this._popLayer.removeChild(node);
                Background.active = false;
            }).start();
        }).start();
    }

    private _lockViewport(lock:boolean,i18nKey:string){
        App.opacity(this._waitNode,0);
        this._maskLayer.active = lock;
        tween(this._maskLayer)
        .call(()=>{
            lock && (this._waitText.string = App.isEmpty(i18nKey)?'':App.i18n.getString(i18nKey));
            this._waitNode.active = lock;
            App.opacity(this._waitNode,lock?255:0,lock?0.1:0);
        })
        .delay(lock?30:0)
        .call(()=>{
            App.opacity(this._waitNode,0,0);
            this._waitNode.active = false;
            this._maskLayer.active = false;
            this._waitText.string = '';
        })
        .start();
    }

    private _resizeViewport(){
        const size:Size = view.getVisibleSize();
        this._rootNode.getComponentsInChildren(Widget).forEach((widget:Widget)=>{
            widget.updateAlignment();
        });
    }

    private _findView(key:string,cls:string):Component{
        const map:Map<string,Component> = this._viewMap.get(key);
        return !App.isInvalid(map) ? map.get(cls) : null;
    }

    private _loadPrefab(name:string):Promise<Component>{
        const subs:string[] = name.split(':');
        const path:string = `${subs.length > 1 ? `res/`:''}prefabs/views/${subs[0]}`;
        return new Promise((res)=>{
            const old:Component = this._findView(subs[1]||COMMON,path);
            if (!App.isInvalid(old)){
                res(old);
                return ;
            }
            App.Asset.load(`${path}${subs.length > 1 ? `:${subs[1]}`:''}`, (prefab:Prefab) => {
                const node:Node = instantiate(prefab);
                const obj:Component = node.getComponent(subs[0]);
                if (App.isInvalid(obj)){
                    throw new Error(`ViewManger._loadPrefab(${name}) not has Component ${subs[0]}`);
                }
                let map:Map<string,Component> = this._viewMap.get(subs[1]||COMMON);
                if (App.isInvalid(map)) map = new Map<string,Component>();
                map.set(path,obj);
                res(obj);
            },Prefab);
        });
    }
}